/* ESP32C3 GPIO Driver Implementation */

#include "gpio.h"
#include <stdint.h>

/* GPIO registers base address */
#define GPIO_BASE_ADDR 0x60004000

/* GPIO registers definition */
typedef struct {
    uint32_t OUT_DATA;          /* Offset: 0x00 */
    uint32_t OUT_ENABLE;        /* Offset: 0x04 */
    uint32_t OUT_ENABLE_W1TS;   /* Offset: 0x08 */
    uint32_t OUT_ENABLE_W1TC;   /* Offset: 0x0C */
    uint32_t OUT_W1TS;          /* Offset: 0x10 */
    uint32_t OUT_W1TC;          /* Offset: 0x14 */
    uint32_t IN_DATA;           /* Offset: 0x18 */
    uint32_t STATUS;            /* Offset: 0x1C */
    uint32_t STATUS_W1TC;       /* Offset: 0x20 */
    uint32_t PIN[16];           /* Offset: 0x24 - 0x60 */
    uint32_t FUN_SEL[16];       /* Offset: 0x64 - 0xA0 */
    uint32_t FUN_IE[16];        /* Offset: 0xA4 - 0xE0 */
    uint32_t FUN_WPD[16];       /* Offset: 0xE4 - 0x120 */
    uint32_t FUN_WPU[16];       /* Offset: 0x124 - 0x160 */
    uint32_t FUN_IE_W1TS[16];   /* Offset: 0x164 - 0x1A0 */
    uint32_t FUN_IE_W1TC[16];   /* Offset: 0x1A4 - 0x1E0 */
    uint32_t RTC_CALIB;         /* Offset: 0x1E4 */
    uint32_t RTC_CALIB_W1TS;    /* Offset: 0x1E8 */
    uint32_t RTC_CALIB_W1TC;    /* Offset: 0x1EC */
} gpio_reg_t;

#define GPIO ((gpio_reg_t *)GPIO_BASE_ADDR)

/* GPIO initialization */
void rt_gpio_init(void)
{
    /* Enable GPIO clock */
    /* TODO: Implement clock enable */
}

/* Set GPIO mode */
void rt_gpio_set_mode(gpio_num_t gpio_num, gpio_mode_t mode)
{
    if (gpio_num >= GPIO_NUM_MAX) {
        return;
    }
    
    switch (mode) {
        case GPIO_MODE_INPUT:
            GPIO->OUT_ENABLE_W1TC = (1 << gpio_num);
            break;
        case GPIO_MODE_OUTPUT:
            GPIO->OUT_ENABLE_W1TS = (1 << gpio_num);
            break;
        case GPIO_MODE_INPUT_OUTPUT:
            /* Already handled by default */
            break;
    }
}

/* Set GPIO pull mode */
void rt_gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull_mode)
{
    if (gpio_num >= GPIO_NUM_MAX) {
        return;
    }
    
    switch (pull_mode) {
        case GPIO_PULLUP_ENABLE:
            GPIO->FUN_WPU[gpio_num] = 1;
            break;
        case GPIO_PULLUP_DISABLE:
            GPIO->FUN_WPU[gpio_num] = 0;
            break;
        case GPIO_PULLDOWN_ENABLE:
            GPIO->FUN_WPD[gpio_num] = 1;
            break;
        case GPIO_PULLDOWN_DISABLE:
            GPIO->FUN_WPD[gpio_num] = 0;
            break;
    }
}

/* Set GPIO level */
void rt_gpio_set_level(gpio_num_t gpio_num, uint8_t level)
{
    if (gpio_num >= GPIO_NUM_MAX) {
        return;
    }
    
    if (level) {
        GPIO->OUT_W1TS = (1 << gpio_num);
    } else {
        GPIO->OUT_W1TC = (1 << gpio_num);
    }
}

/* Get GPIO level */
uint8_t rt_gpio_get_level(gpio_num_t gpio_num)
{
    if (gpio_num >= GPIO_NUM_MAX) {
        return 0;
    }
    
    return (GPIO->IN_DATA >> gpio_num) & 0x01;
}
